#include "lvgl/lvgl.h"
#include "lvgl/demos/lv_demos.h"
#include "lv_drivers/display/sunxifb.h"
#include "lv_drivers/indev/evdev.h"
#include <unistd.h>
#include <pthread.h>
#include <time.h>
#include <sys/time.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>

bool pcfs_ready(lv_fs_drv_t* drv)
{
    (void)drv; /*Unused*/
    return true;  //这里仅返回true,如果是嵌入式，则是返回嵌入式文件系统挂载成功与否的标志
}
static void* pcfs_open(lv_fs_drv_t* drv, const char* fn, lv_fs_mode_t mode)
{
    (void)drv; /*Unused*/

    errno = 0;

    const char* flags = "";

    if (mode == LV_FS_MODE_WR) flags = "wb";
    else if (mode == LV_FS_MODE_RD) flags = "rb";
    else if (mode == (LV_FS_MODE_WR | LV_FS_MODE_RD)) flags = "a+";

    /*Make the path relative to the current directory (the projects root folder)*/
    char buf[256];
    sprintf(buf, "./%s", fn);

    return fopen(buf, flags);  
}
static lv_fs_res_t pcfs_write(struct _lv_fs_drv_t* drv, void* file_p, const void* buf, uint32_t btw, uint32_t* bw)
{
    (void)drv; /*Unused*/

    *bw = (uint32_t)fwrite(buf, 1, btw, file_p);

    return LV_FS_RES_OK;
}
static lv_fs_res_t pcfs_read(lv_fs_drv_t* drv, void* file_p, void* buf, uint32_t btr, uint32_t* br)
{
    (void)drv; /*Unused*/

    //pc_file_t* fp = file_p;        /*Just avoid the confusing casings*/
    *br = (uint32_t)fread(buf, 1, btr, file_p);
    return LV_FS_RES_OK;
}
static lv_fs_res_t pcfs_close(lv_fs_drv_t* drv, void* file_p)
{
    (void)drv; /*Unused*/

    return fclose(file_p);
}

static lv_fs_res_t pcfs_seek(lv_fs_drv_t* drv, void* file_p, uint32_t pos,lv_fs_whence_t whence)
{
    (void)drv; /*Unused*/

    return fseek(file_p, pos, whence);
}
static lv_fs_res_t pcfs_tell(lv_fs_drv_t* drv, void* file_p, uint32_t* pos_p)
{
    (void)drv; /*Unused*/
    *pos_p = ftell(file_p);
    return LV_FS_RES_OK;
}

int main(int argc, char *argv[])
{
    static lv_disp_drv_t disp_drv;
    lv_disp_drv_init(&disp_drv);

    /*LittlevGL init*/
    lv_init();

#if USE_SUNXIFB
    uint32_t rotated = LV_DISP_ROT_90;
    sunxifb_init(rotated);
#endif

    /*A buffer for LittlevGL to draw the screen's content*/
    static uint32_t width, height;
    sunxifb_get_sizes(&width, &height);

    static lv_color_t *buf;
    buf = (lv_color_t*) sunxifb_alloc(width * height * sizeof(lv_color_t),
            "lv_examples");

    if (buf == NULL) {
        sunxifb_exit();
        printf("malloc draw buffer fail\n");
        return 0;
    }

    /*Initialize a descriptor for the buffer*/
    static lv_disp_draw_buf_t disp_buf;
    lv_disp_draw_buf_init(&disp_buf, buf, NULL, width * height);

    /*Initialize and register a display driver*/
    disp_drv.draw_buf   = &disp_buf;
    disp_drv.flush_cb   = sunxifb_flush;
    disp_drv.hor_res    = width;
    disp_drv.ver_res    = height;
    disp_drv.rotated    = rotated;
#ifndef USE_SUNXIFB_G2D_ROTATE
    if (rotated != LV_DISP_ROT_NONE)
        disp_drv.sw_rotate = 1;
#endif
    lv_disp_drv_register(&disp_drv);

    static lv_fs_drv_t pcfs_drv;                         /*A driver descriptor*/
    lv_fs_drv_init(&pcfs_drv);
    pcfs_drv.user_data = NULL;
    pcfs_drv.letter = 'B';
    pcfs_drv.ready_cb = pcfs_ready;
    pcfs_drv.write_cb = pcfs_write;
    pcfs_drv.open_cb = pcfs_open;
    pcfs_drv.close_cb = pcfs_close;
    pcfs_drv.read_cb = pcfs_read;
    pcfs_drv.seek_cb = pcfs_seek;
    pcfs_drv.tell_cb = pcfs_tell;
    lv_fs_drv_register(&pcfs_drv);

    evdev_init();
    static lv_indev_drv_t indev_drv;
    lv_indev_drv_init(&indev_drv);                /*Basic initialization*/
    indev_drv.type =LV_INDEV_TYPE_POINTER;        /*See below.*/
    indev_drv.read_cb = evdev_read;               /*See below.*/
    /*Register the driver in LVGL and save the created input device object*/
    lv_indev_t * evdev_indev = lv_indev_drv_register(&indev_drv);

    ui_init();

    /*Handle LitlevGL tasks (tickless mode)*/
    while(1) {
        lv_task_handler();
        usleep(5000);
    }

    /*sunxifb_free((void**) &buf, "lv_examples");*/
    /*sunxifb_exit();*/
    return 0;
}

/*Set in lv_conf.h as `LV_TICK_CUSTOM_SYS_TIME_EXPR`*/
uint32_t custom_tick_get(void) {
    static uint64_t start_ms = 0;
    if (start_ms == 0) {
        struct timeval tv_start;
        gettimeofday(&tv_start, NULL);
        start_ms = ((uint64_t) tv_start.tv_sec * 1000000
                + (uint64_t) tv_start.tv_usec) / 1000;
    }

    struct timeval tv_now;
    gettimeofday(&tv_now, NULL);
    uint64_t now_ms;
    now_ms = ((uint64_t) tv_now.tv_sec * 1000000 + (uint64_t) tv_now.tv_usec)
            / 1000;

    uint32_t time_ms = now_ms - start_ms;
    return time_ms;
}
